package services.venta;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.springframework.transaction.annotation.Transactional;

import services.historico.VentaHistoricoService;
import util.assembly.VentaAssembly;
import bo.Cliente;
import bo.ItemFactura;
import bo.MovimientoStock;
import bo.PersistentObject;
import bo.Venta;
import dao.ClienteDAO;
import dao.MovimientoStockDAO;
import dao.ProductoDAO;
import dao.VentaDAO;
import dto.SearchDTO;
import dto.VentaDTO;
import exceptions.CannotDeleteException;
import exceptions.CannotSaveException;
import exceptions.EntityNotFoundException;


public class VentaService {
	
	VentaHistoricoService ventaHistoricoService;
	
	MovimientoStockDAO movimientoStockDAO;
	
	VentaDAO ventaDAO;
	
	ProductoDAO productoDAO;
	
	ClienteDAO clienteDAO;
	
	
	/**
	 * Guarda una venta
	 * @param ventaDTO	DTO de venta
	 */
	@Transactional
	public Venta save(VentaDTO ventaDTO) throws CannotSaveException {
		
		Venta oldVenta = null;
		
		Venta venta = VentaAssembly.toBO(ventaDTO);
		
		Set<ItemFactura> items = venta.getFactura().getItems();
		for (ItemFactura itemFactura : items) {
			itemFactura.setFactura(venta.getFactura());
		}
		
		Cliente cliente = (Cliente) clienteDAO.findById(Cliente.class, venta.getCliente().getId());
		venta.setCliente(cliente);
		
		// Es un UPDATE
		boolean isUpdate = false;
		if (venta.getId() != null && !"".equals(venta.getId())) {
		
			isUpdate = true;

			oldVenta = (Venta)ventaDAO.findById(Venta.class, venta.getId());	// Me quedo con la venta de la DB para usarla despues
			ventaDAO.getHibernateTemplate().evict(oldVenta);
		}	
		
		// Finalmente se guarda la venta
		String id = ventaDAO.save(venta);
		if (id == null) {
			throw new CannotSaveException();
		}
		ventaDAO.getHibernateTemplate().flush();
		
		// CREACION
		if (!isUpdate) {
			
			// Es una creacion con RETIRO INMEDIATO
			if ( Venta.RETIRO_INMEDIATO.equals(venta.getRetiro()) ) {
				Venta v = (Venta)ventaDAO.findById(Venta.class, id);
				guardarMovimientoStock(v);
			}
		
		// UPDATE
		} else {
			
			// Si pasa de RETIRO PACTADO a INMEDIATO genero un movimiento de stock
			String oldRetiro = oldVenta.getRetiro();
			String newRetiro = venta.getRetiro();

			ventaDAO.getHibernateTemplate().flush();
			if ( Venta.RETIRO_PACTADO.equals(oldRetiro) && Venta.RETIRO_INMEDIATO.equals(newRetiro) ) {	
				guardarMovimientoStock(venta);
			}
		}
		
		return venta;
	}

	
	/**
	 * Borra un venta
	 * @param id	ID del venta a borrar
	 * @throws CannotDeleteException
	 */
	@Transactional
	public void delete(String id) throws CannotDeleteException {
		
		Venta venta = (Venta) ventaDAO.findById(Venta.class, id);
		
		// Nuleo para evitar que se propague el delete
		venta.setEntrega(null);
		venta.setFactura(null);
		
		ventaDAO.delete(venta);
	}
	
	
	@Transactional
	public List<VentaDTO> search(SearchDTO searchDTO) {
		
		List<VentaDTO> lista = new ArrayList<VentaDTO>();
		
		List<PersistentObject> ventas = ventaDAO.search(Venta.class, searchDTO);
		for (PersistentObject persistentObject : ventas) {
			lista.add(VentaAssembly.toDTO((Venta)persistentObject));
		}
		return lista;
	}
	
	
	
	/**
	 * Obtiene todos los productos
	 * @return	la lista de ventas
	 */
	@Transactional
	public List<VentaDTO> findAll() {
		
		List<VentaDTO> lista = new ArrayList<VentaDTO>();
		
		List<PersistentObject> ventas = ventaDAO.findAll(Venta.class);
		for (PersistentObject persistentObject : ventas) {
			lista.add(VentaAssembly.toDTO((Venta)persistentObject));
		}
		return lista;
	}
	
	/**
	 * Obtiene todos los productos ordenados por un atributo
	 * @return	la lista de ventas
	 */
	@Transactional
	public List<VentaDTO> findAll(String order, boolean asc) {
		
		List<VentaDTO> lista = new ArrayList<VentaDTO>();
		
		List<PersistentObject> ventas = ventaDAO.findAll(Venta.class, order, asc);
		for (PersistentObject persistentObject : ventas) {
			lista.add(VentaAssembly.toDTO((Venta)persistentObject));
		}
		return lista;
	}
	
	
	/**
	 * Obtiene un venta segun ID
	 * @param id	el ID
	 * @return el venta
	 */
	@Transactional
	public VentaDTO findById(String id) throws EntityNotFoundException {
		
		Venta venta = (Venta) ventaDAO.findById(Venta.class, id);
		
		if (venta == null) {
			throw new EntityNotFoundException();
		}
		VentaDTO ventaDTO = VentaAssembly.toDTO(venta);
		return ventaDTO;
	}
	

	private void guardarMovimientoStock(Venta venta) {
		MovimientoStock movimientoStock = new MovimientoStock();
		movimientoStock.setFecha(new java.util.Date());
		Integer cantidad = getCantidadVentaRelacionada(venta);
		movimientoStock.setCantidad(cantidad);
		movimientoStock.setVenta(venta);
		movimientoStockDAO.save(movimientoStock);
	}
	
	private Integer getCantidadVentaRelacionada(Venta venta) {
		Integer acum = 0;
		
		Set<ItemFactura> itemFacturaSet = venta.getFactura().getItems();
		for (ItemFactura itemFactura : itemFacturaSet) {
			acum += itemFactura.getCantidad();
		}
		return acum * -1;
	}
	
	
	public ClienteDAO getClienteDAO() {
		return clienteDAO;
	}

	public void setClienteDAO(ClienteDAO clienteDAO) {
		this.clienteDAO = clienteDAO;
	}

	public ProductoDAO getProductoDAO() {
		return productoDAO;
	}

	public void setProductoDAO(ProductoDAO productoDAO) {
		this.productoDAO = productoDAO;
	}

	public VentaDAO getVentaDAO() {
		return ventaDAO;
	}

	public void setVentaDAO(VentaDAO ventaDAO) {
		this.ventaDAO = ventaDAO;
	}


	public VentaHistoricoService getVentaHistoricoService() {
		return ventaHistoricoService;
	}


	public void setVentaHistoricoService(VentaHistoricoService ventaHistoricoService) {
		this.ventaHistoricoService = ventaHistoricoService;
	}

	public MovimientoStockDAO getMovimientoStockDAO() {
		return movimientoStockDAO;
	}


	public void setMovimientoStockDAO(MovimientoStockDAO movimientoStockDAO) {
		this.movimientoStockDAO = movimientoStockDAO;
	}
	
}
